package com.example.spring_mybatis.config;

import org.apache.ibatis.builder.annotation.ProviderSqlSource;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;

/**
 * @author onoo
 * @create 2021-08-16
 **/
@Configuration
@AutoConfigureAfter(MybatisAutoConfiguration.class)
public class MapperConfig {

    @Autowired
    private List<SqlSessionFactory> sqlSessionFactories;

    @PostConstruct
    public void init(){
        for (SqlSessionFactory sessionFactory : sqlSessionFactories) {
            providerSqlSourceHandler(sessionFactory.getConfiguration ());
        }
    }

    private void providerSqlSourceHandler(org.apache.ibatis.session.Configuration configuration) {
        Collection<MappedStatement> mappedStatements = configuration.getMappedStatements ();
        for (MappedStatement ms : mappedStatements) {
            SqlSource sqlSource = ms.getSqlSource ();
            if (sqlSource instanceof ProviderSqlSource) {
                ProviderSqlSource providerSqlSource = (ProviderSqlSource) sqlSource;
                MetaObject msObject = SystemMetaObject.forObject(providerSqlSource);
                Class<?> providerType = (Class<?>) msObject.getValue ("providerType");
                Method mapperMethod = (Method) msObject.getValue ("mapperMethod");
                try {
                    Object provider = providerType.newInstance ();
                    int i = mapperMethod.getName ().lastIndexOf (".");
                    String methodName = mapperMethod.getName ().substring (i+1);
                    Method method = providerType.getMethod (methodName, MappedStatement.class);
                    method.invoke (provider,ms);
                } catch (Exception e) {
                    e.printStackTrace ();
                }
            }
        }
    }

}
